<!-- eslint-disable vue/no-multiple-template-root -->
<template>
  <div
    v-loading="loading"
    ref="boxRef"
    :class="[
      'box',
      noBorder && 'no-right-border',
      list[0]?.level === 1 && activeItem.name && 'no-right-border',
      hasBorder && 'border-right'
    ]"
  >
    <div :class="['tag-title']">
      <span class="pl16">{{ titleInfo.title || TITLE_MAP.get(titleInfo.level) || ONE_LEVEL_TAG }}</span>
      <ul :class="['tag-list', 'add-icon-box']" v-if="!hideAdd">
        <span @click.stop="() => addTag(list)" class="add-icon">+<span class="ml10 fs14">添加标签</span></span>
      </ul>
    </div>
    <RecycleScroller
      v-if="list.length"
      key-field="_id"
      :class="['tag', 'tag-main']"
      page-mode
      :items="list"
      emitUpdate
      v-slot="{ item: tag }"
    >
      <div class="tag-list-box" @click="() => setActive(tag, list)">
        <ul class="tag-list" v-if="tag.color">
          <li :class="['tag-item', tag.active && 'gray-bg', getComputedBox(tag)]">
            <div class="flex space-between fullHeight" v-if="!tag.isEdit">
              <el-tooltip :append-to="box" placement="top" :hide-after="0" :show-after="500" :content="tag.name">
                <span
                  class="tag-item-name"
                  :style="{
                    background: tag.color,
                    color: tag.color === '#FFFFFF' || tag.color === '#fff' || !tag.color ? '#252c32' : '#ffffff'
                  }"
                  >{{ tag.name }}</span
                >
              </el-tooltip>
              <span class="tag-item-icon-box">
                <el-switch
                  @change="(status: any) => confirmEdit(tag, {status})"
                  @click.stop=""
                  :active-value="1"
                  :inactive-value="0"
                  v-model="tag.status"
                />
                <p class="ml10 noMargin pick-color-wrapper">
                  <el-color-picker
                    @click.stop=""
                    @change="(color: any) => confirmEdit(tag, {color})"
                    popper-class="pick-color-box"
                    v-model="tag.color"
                    :predefine="predefineColors"
                  />
                </p>
                <i @click.stop="() => onChangeEdit(tag)" class="MC_iconfont MC_icon-bianji1-xian gray-color ml10"></i>
                <i @click.stop="() => onDel(tag, list)" class="MC_iconfont MC_icon-shanchu1-xian gray-color ml10"></i>
              </span>
            </div>
            <div v-else>
              <el-input
                v-focus
                maxlength="50"
                @click.stop=""
                @keydown.enter="
                    (e: any) => {
                      e.stopPropagation()
                      confirmEdit(tag, { list })
                    }
                  "
                v-model.trim="tag.editName"
              >
                <template #append>
                  <span @click.stop="() => closeEdit(tag, list)" class="icon-box">
                    <i class="MC_iconfont MC_icon-quxiao-mian gray-icon small-icon"></i>
                  </span>
                  <span @click.stop="() => confirmEdit(tag, { list })" class="icon-box ml10">
                    <i class="MC_iconfont MC_icon-wancheng-mian gray-icon small-icon"></i>
                  </span>
                </template>
              </el-input>
            </div>
          </li>
          <!-- <li v-if="showEmpty(tag)" class="tag-item">空标签</li> -->
        </ul>
      </div>
    </RecycleScroller>
  </div>
  <div @click="() => addNextLevel(activeItem as any)" v-if="showNextAdd" class="add-next-level add-icon flex">+</div>
  <div class="table-class">
    <tag-list
      :getList="getList"
      :parent="activeItem"
      :value="activeItem?.children"
      :total="total"
      :keyWords="keyWords"
      :status="status"
      :queryKeys="queryKeys"
      :updateTagTotal="updateTagTotal"
      v-if="activeItem?.children?.length"
    />
  </div>
</template>
<script lang="ts" setup>
import { ElMessage, ElMessageBox } from 'element-plus'
import { isEmpty, set } from 'lodash'
import { withDefaults, defineProps, reactive, toRefs, ref, computed, watch, markRaw, nextTick } from 'vue'
import { LEVEL_LIMIT, TITLE_MAP, COUNT_LIMIT, INIT_DATA, EMPTY, ONE_LEVEL_TAG } from './constance'
import { TagNode } from '@/common/interface/node'
import http from '@/common/utils/http'
import { focus as vFocus } from '@/app/base/directives/input-focus'
import mcGetCache from '@/common/utils/mc-get-cache'
import 'vue-virtual-scroller/dist/vue-virtual-scroller.css'
import { RecycleScroller } from 'vue-virtual-scroller'
import { LIMIT_LEVEL } from '../../../agent/enterprise-staff/index/constance'
import deleteIcon from '@/common/components/delete-icon/index.vue'
import { newDate } from '@/common/utils/common'
interface Props {
  value: any
  parent?: any
  getList: () => any
  total: number
  updateTagTotal: (arg?: boolean) => void
  queryKeys: any // 关键词搜索key
  keyWords: any
  status: any
  box?: any
}
interface State {
  list: TagNode[]
  storeData: Record<string, unknown>[]
  loading: boolean
}
const props = withDefaults(defineProps<Props>(), {
  value: [],
  total: 0,
  keyWords: ''
})
const state: State = reactive({
  list: [],
  storeData: [],
  loading: false
})

const predefineColors = ref([
  '#FFFFFF',
  'var(--mc-main-color)',
  '#F76659',
  '#FFA801',
  '#FFD32A',
  '#2ECC71',
  '#4094F7',
  '#1ABC9C',
  '#C479F3',
  '#808E9B',
  '#34495E'
])

const { list, loading } = toRefs(state)
const activeItem: any = computed(() => list.value.find((i: TagNode) => i.active))
const hideAdd = computed(() => props.total >= COUNT_LIMIT)
const showNextAdd = computed(() => {
  const { active, level, isLeaf, parent } = activeItem.value || {}
  return (
    ((active && level !== LEVEL_LIMIT && isLeaf) || (level !== LEVEL_LIMIT && level !== 1 && !parent)) &&
    !hideAdd.value &&
    activeItem.value?.name &&
    ((list.value.length === 1 && list.value[0].color) || list.value.length > 1)
  )
})
const noBorder = computed(
  () =>
    (list.value[0]?.level !== 1 && list.value[0].level !== LIMIT_LEVEL && list.value[0].name) ||
    (list.value[0]?.level === 1 && !list.value[0]?.children?.length)
)
const hasBorder = computed(() => hideAdd.value && !activeItem.value?.children?.length)
const titleInfo = computed(() => list.value[0])
const boxRef: any = ref(null)
watch(
  () => props.value,
  () => {
    state.list = props.value
    set(state.list[0], 'title', TITLE_MAP.get(state.list[0]?.level || 1))
  },
  { immediate: true, deep: true }
)

const onChangeEdit = (data: any) => {
  data.isEdit = !data.isEdit
}

const getComputedBox = (tag: TagNode) => {
  if (((props.keyWords || props.status !== EMPTY) && props.queryKeys.size) || props.status !== EMPTY) {
    return props.queryKeys.has(tag._id) ? 'show' : 'hide'
  }
  return 'show'
}

const deleteItem = async (data: TagNode, list?: TagNode[]) => {
  if (!list) {
    return
  }
  data.active = false
  const index = list.findIndex((i: TagNode) => i._id === data._id)
  list.splice(index, 1)
  await nextTick()
  if (!state.list.length) {
    state.list = INIT_DATA
  }
  if (!list.length) {
    props.parent.isLeaf = true
    list = [{ title: data.title || TITLE_MAP[data?.level || ''], name: '' }]
  }
  updateActive(list)
}

const onDel = (data: TagNode, list: TagNode[]) => {
  const _id: any = data._id
  ElMessageBox.confirm(`确定删除该标签${data.level === LEVEL_LIMIT ? '' : '及下级标签'}？`, '删除', {
    confirmButtonText: '确定',
    cancelButtonText: '取消',
    type: 'warning',
    icon: markRaw(deleteIcon)
  }).then(async () => {
    await http({
      url: '/platform/action/console/service-label/delete',
      method: 'POST',
      data: {
        _id
      }
    })
    ElMessage.success('操作成功')
    deleteItem(data, list)
    mcGetCache.$deleteCache('serviceLabels', '_id', _id, 'hasObj')
    props.updateTagTotal(false)
  })
}
const closeEdit = (data: TagNode, list?: TagNode[]) => {
  if (!data.name) {
    deleteItem(data, list)
    toBottom()
  }
  data.isEdit = false
  data.editName = data.name
}

const childrenBatch = (data: TagNode, keyMap: Record<string, unknown> | string) => {
  const { children } = data
  Object.entries(keyMap).forEach(([key, val]: any) => {
    set(data, key, val)
  })
  if (isEmpty(children)) {
    return
  }
  children?.forEach((i: TagNode) => {
    Object.entries(keyMap).forEach(([key, val]: any) => {
      set(i, key, val)
    })
    if (!isEmpty(i.children)) {
      i.children?.forEach((m: TagNode) => childrenBatch(m, keyMap))
    }
  })
}

/**
 * 添加和删除更新active项
 */
const updateActive = (list?: TagNode[]) => {
  setTimeout(() => {
    const noTitleList = list?.filter((i: TagNode) => i.name)
    const noActive = noTitleList?.every((i: TagNode) => !i.active)
    if (noActive && noTitleList?.length) {
      noTitleList[0].active = true
    }
  })
}

const confirmEdit = async (data: TagNode, options?: Partial<TagNode>) => {
  const { editName, name, color, _id, status } = data
  const parent = list.value[0].parent
  let trimStr = null
  trimStr = editName?.trim()
  if (!trimStr) {
    return ElMessage.error('内容不能为空')
  }
  const cmpName = trimStr ?? name
  if (cmpName?.length > 50) {
    return ElMessage.error('标签长度不超过50个字符')
  }
  http({
    url: `/platform/action/console/service-label/${data.name ? 'update' : 'create'}`,
    method: 'POST',
    data: {
      name: cmpName,
      color: options?.color ?? color,
      status: options?.status ?? status,
      parent,
      ...(data.name ? { _id } : {})
    }
  }).then((res) => {
    ElMessage.success('操作成功')
    const isCreate = !data.name
    if (isCreate) {
      data.isLeaf = true
      props.updateTagTotal(true)
    }
    mcGetCache.$getCache('serviceLabels', 'hasObj', '', 'formServer')
    data.color = res.data.color
    data.status = res.data.status
    updateActive(options?.list)
    if (res.data) {
      data.name = res.data.name
      data._id = res.data._id
    } else {
      data.editName && (data.name = data.editName)
    }
    if (options?.list?.[0].level) {
      data.level = options?.list?.[0].level
    }
    if (!data.isLeaf) {
      childrenBatch(data, {
        ...(data.status === 0 ? { status: 0 } : {}),
        color: data.color
      })
    }
    data.isEdit = false
    props.queryKeys.add(data._id)
  })
}
const setActive = (tag: TagNode, list: TagNode[]) => {
  list.forEach((i: TagNode) => {
    i.active = false
  })
  if (!tag.children?.length) {
    tag.isLeaf = true
  }
  tag.active = true
}

const toBottom = () => {
  setTimeout(() => {
    boxRef.value.scrollTo({
      top: 99999999,
      behavior: 'smooth'
    })
  })
}
const addTag = (list: TagNode[]) => {
  const { level, isLeaf, twoLevel, parent } = list[0]
  const hasQuery = props.keyWords || props.status !== EMPTY
  const _id = String(+newDate())
  if (hasQuery) {
    props.queryKeys.add(_id)
  }
  const color = props.parent?.color || '#fff'
  if (list.length === 1 && !list[0].color) {
    props.queryKeys.add(list[0]._id)
    Object.assign(list[0], {
      parent,
      isEdit: true,
      editName: '',
      name: '',
      level,
      isLeaf: isLeaf ?? level === 1,
      twoLevel,
      status: 1,
      color,
      size: 53
    })
    return
  }
  list.push({
    _id,
    parent,
    isEdit: true,
    editName: '',
    name: '',
    level,
    isLeaf: isLeaf ?? level === 1,
    twoLevel,
    status: 1,
    color,
    size: 53
  })
  toBottom()
}

const addNextLevel = (tag: TagNode) => {
  setTimeout(() => {
    if (tag.isLeaf) {
      tag.isLeaf = false
    }
    const _id = String(newDate())
    Object.assign(tag, {
      children: [
        {
          _id,
          parent: tag._id || state.list[1]._id,
          name: '',
          active: true,
          isLeaf: true,
          level: Number(tag.level) + 1,
          title: TITLE_MAP.get(Number(tag.level) + 1)
        }
      ]
    })
  })
}
</script>
<style lang="stylus" scoped>
.table-class
  position: relative;
  display flex
.no-border-bottom
  border-bottom none !important
.no-right-border
  border-right none !important
.pl16
  padding-left 16px
.add-icon-box
  border-top: 1px solid var(--mc-gray4-color)
  padding-left: 16px
.border-right
  border-right 1px solid var(--mc-gray4-color) !important
.deep-box
  float: left;
  position: absolute;
  left: calc(20vw - 50px);
  top: 0;
  .box
    border-top: none;
    border-right: none;
    border-bottom: none;
.box
  border: 1px solid var(--mc-gray4-color);
  height: calc(100vh - 270px);
  max-width 1150px
  width: calc(20vw - 60px);
  overflow-y scroll
  position: relative;

.box::-webkit-scrollbar

  width:0px;

  height:0px;

  background-color:#fff;

  /*定义滚动条轨道

  内阴影+圆角*/

.box::-webkit-scrollbar-track

  -webkit-box-shadow:inset 0 0 0px #fff;

  border-radius:0px;

  background-color:#fff;


  /*定义滑块

  内阴影+圆角*/

.box::-webkit-scrollbar-thumb

  border-radius:0px;

  -webkit-box-shadow:inset 0 0 0px #fff;

  background-color:#fff;

.no-border-top
  border-top 0 !important
.tag
  display flex
.tag-main
  display: flex;
  flex-direction: column;
  padding: 0;
  margin: 0;
  overflow: hidden;
.left-border-radius
  border-radius 4px 0 0 4px
.right-border-radius
  border-radius 0 4px 4px 0
.pick-color-wrapper
  cursor pointer
  :deep(.el-color-picker)
    width: 14px !important;
    height: 14px !important;
    position: relative;
    top: 2px;
    border-radius: 50%;
    background: radial-gradient(43.44% 43.44% at 50% 50%, #FFFFFF 0%, rgba(255, 255, 255, 0.0001) 100%) /* warning: gradient uses a rotation that is not supported by CSS and may not behave as expected */, conic-gradient(from 90deg at 50% 50%, #FF0606 -1.92deg, #FF06E8 46.47deg, #120BFF 116.9deg, #0AD5FF 163.94deg, #11FEFF 178.78deg, #10F93B 222.31deg, #09FF08 248.2deg, #FFFA06 302.85deg, #FF0606 358.08deg, #FF06E8 406.47deg);
  :deep(.el-color-picker__trigger)
    height: 14px;
    width: 14px;
    border: none;
  :deep(.el-color-picker__color)
    display none
.pick-color-box
  background unset
  .el-color-predefine__color-selector
    border-radius: 50%;
.tag-title
  border-bottom: 1px solid var(--mc-gray4-color);
  font-size: 14px;
  line-height: 53px;
  color: rgba(0,0,0,0.88);
  font-weight: 500;
  position: sticky;
  top: 0;
  width: 100%;
  background: #fff;
  z-index: 999;
  display: flex;
  flex-direction: column;
.tag-list-box
  cursor pointer
  width: calc(20vw - 88px);

.tag-list
  width 100%
  padding 0
  margin 0
.tag-item
  height 52px
  line-height 52px
  border-bottom 1px solid var(--mc-gray4-color)
  list-style none
  padding 0 10px
  padding-right 12px
  width: calc(20vw - 80px);
  :deep(.el-input__wrapper)
    box-shadow: none;
    border: 1px solid var(--el-border-color);
    border-right: none;
    height: 38px !important;
  :deep(.el-input-group__append)
    background-color: #fff !important;
    box-shadow: unset;
    border: 1px solid var(--el-input-border-color);
    border-left: unset;
.add-icon
  font-size: 20px;
  color: var(--el-color-primary-dark);
  font-weight: bold;
  cursor: pointer;
  margin-left: 16px;
  display: inline-block;
  height: 53px;
.fs14
  font-size 14px
.mt20
  margin-top 20px
.ml10
  margin-left 10px
.small-icon
  color var(--el-input-border-color) !important
  font-size 14px !important
.color-pick
  display inline-block
  height 14px
  width 14px
  border-radius 50%
  cursor pointer
  background radial-gradient(43.44% 43.44% at 50% 50%, #FFFFFF 0%, rgba(255, 255, 255, 0.0001) 100%) /* warning: gradient uses a rotation that is not supported by CSS and may not behave as expected */, conic-gradient(from 90deg at 50% 50%, #FF0606 -1.92deg, #FF06E8 46.47deg, #120BFF 116.9deg, #0AD5FF 163.94deg, #11FEFF 178.78deg, #10F93B 222.31deg, #09FF08 248.2deg, #FFFA06 302.85deg, #FF0606 358.08deg, #FF06E8 406.47deg)
.gray-color
  color rgba(0, 0, 0, 0.26)
  font-size 14px
.tag-item-icon-box
  display flex
  align-items center
.ml10
  margin-left 10px !important

.noMargin
  margin 0

.fullHeight
  height 100%
.space-between
  justify-content space-between !important
.flex
  display flex
  align-items center
  justify-content center
.gray-bg
   background: var(--mc-gray4-color);
.gray-icon
  color rgba(0, 0, 0, 0.26);
.add-next-level
  width: 42px;
  cursor: pointer;
  border: 1px solid var(--mc-gray4-color);
  border-right: 1px solid var(--mc-gray4-color);
  margin-left: 0;
  height: calc(100vh - 270px);
.line
  width 1px
  height 100%
  position: absolute;
  left: calc(20vw - 50px);
  top: 0;
  background var(--mc-gray4-color)
.tag-item-name
  display: inline-block;
  height: 30px;
  border: 1px solid var(--mc-gray4-color);
  border-radius: 15px;
  line-height: 16px;
  min-width: 80px;
  max-width: 45%;
  text-align: center;
  color: #252c32;
  padding: 5px;
  box-sizing: border-box;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;

.icon-box
  color var(--el-color-primary-dark)
  font-size 10px
  cursor pointer
  i
    color: #fff;
    height: 14px;
    width: 14px;
    text-align: center;
    line-height: 14px;
    border-radius: 50%;
    font-size: 10px;
 .hide
  display none
 .show
  display block
</style>
